package com.module.third.learcloud

import android.app.Activity
import android.content.Context
import cn.leancloud.*
import cn.leancloud.annotation.LCClassName
import cn.leancloud.core.LeanCloud
import cn.leancloud.json.JSONObject
import cn.leancloud.push.PushService
import cn.leancloud.sms.LCSMS
import cn.leancloud.sms.LCSMSOption
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.module.third.BuildConfig
import com.module.third.ThirdUtil
import com.module.third.bean.lc.LCIgnore
import com.module.third.bean.lc.LCIgnoreProperties
import com.module.third.bean.lc.LCJson
import com.module.third.bean.lc.MyLCObject
import com.module.third.bean.push.Push
import com.module.third.bean.push.PushData
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.suspendCancellableCoroutine
import java.lang.reflect.Field
import kotlin.coroutines.resumeWithException

/**
 * learnCloud工具类，
 * learnCloud只支持rxjava 不支持挂起函数
 * 所以：
 * 常用 withContext(Dispatchers.IO) 来设置挂起函数，内部使用同步
 * 但是出现问题是，LearnCloud内部使用的rxjava,不支持协程取消，
 * 所以超时取消，就无效了
 * 最终方案
 * 使用suspendCancellableCoroutine将rxjava 回调转换为协程，
 * suspendCancellableCoroutine是支持协程取消
 * 主要就满足我们协程可以超时取消
 */

var action: String = "";

/**
 * learnCloud初始化
 */
fun initLearnCloud(myAction: String, appId: String, appKey: String, serverUrl: String) {
    action = myAction
    if (BuildConfig.DEBUG) {
        LeanCloud.setLogLevel(LCLogger.Level.ALL)
    } else {
        LeanCloud.setLogLevel(LCLogger.Level.OFF)
    }
    LeanCloud.initialize(appId, appKey, serverUrl)
}

/**
 *  初始化推送
 */
fun initLCPush(context: Context, mClass: Class<out Activity>, channelId: String) {
    //LeanCloud 推送，默认打开的Activity
    PushService.setDefaultPushCallback(context, mClass)
    PushService.setDefaultChannelId(context, channelId)
}


/**
 * 上传数据
 */
suspend fun <T : MyLCObject> lcSave(o: T, tClass: Class<T>): LCResponse<T> {
    return lcSave(o, null, tClass)
}

/**
 * 更新数据
 *
 * @param objectId null则新增数据， 不为null则更新数据
 */
suspend fun <T : MyLCObject> lcSave(
    o: T,
    objectId: String?,
    tClass: Class<T>
): LCResponse<T> {
    return lcSaveIncrement(o, objectId, tClass, null, 0)
}

suspend fun <T : MyLCObject> lcSaveIncrement(
    tClass: Class<T>,
    objectId: String?,
    incrementKey: String?,
    increment: Number
): LCResponse<T> {
    return suspendCancellableCoroutine { suspend ->
        var lcObject: LCObject = if (objectId.isNullOrEmpty()) {
            LCObject(getClassName(tClass)) // //创建
        } else {
            LCObject.createWithoutData(getClassName(tClass), objectId)//更新
        }
        //增量
        if (!incrementKey.isNullOrEmpty()) {
            lcObject.increment(incrementKey, increment)
        }
        lcObject.isFetchWhenSave = true
        //请求接口
        var observable = lcObject.saveInBackground()
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .doOnError { e ->
                suspend.resumeWithException(e)
            }
            .subscribe { lcObject ->
                //格式转换
                val gson = Gson()
                var json: String = gson.toJson(lcObject.serverData)
                suspend.resume(LCResponse(SUCCESS_CODE, "", gson.fromJson(json, tClass))) {
                }
            }

    }
}

/**
 * 增量的保存数据，如果点赞增量
 */
suspend fun <T : MyLCObject> lcSaveIncrement(
    o: T,
    objectId: String?,
    tClass: Class<T>,
    incrementKey: String?,//允许为空
    increment: Number
): LCResponse<T> {
    return suspendCancellableCoroutine { suspend ->
        var lcObject: LCObject = if (objectId.isNullOrEmpty()) {
            LCObject(getClassName(o)) // //创建
        } else {
            LCObject.createWithoutData(getClassName(o), objectId)//更新
        }
        //增量
        if (!incrementKey.isNullOrEmpty()) {
            lcObject.increment(incrementKey, increment)
        }
        lcObject.isFetchWhenSave = true
        put(lcObject, o, incrementKey)
        //请求接口
        var observable = lcObject.saveInBackground()
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .doOnError { e ->
                suspend.resumeWithException(e)
            }
            .subscribe { lcObject ->
                //格式转换
                val gson = Gson()
                var json: String = gson.toJson(lcObject.serverData)
                suspend.resume(LCResponse(SUCCESS_CODE, "", gson.fromJson(json, tClass))) {

                }
            }

    }
}


/**
 * 批量更新数据-直接返回是否成功
 */
suspend fun <T : MyLCObject> lcSaveAll(list: MutableList<T>): LCResponse<Boolean> {
    return suspendCancellableCoroutine { suspend ->
        check(!list.isNullOrEmpty()) { "数据不能为空" }
        val listObject: MutableList<LCObject> = mutableListOf()
        for (t in list) {
            val lcObject = LCObject.createWithoutData(getClassName(t), t!!.objectId)
            put(lcObject, t, null)
            listObject.add(lcObject)
        }
        // 批量更新
        var observable = LCObject.saveAllInBackground(listObject)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .doOnError { e ->
                suspend.resumeWithException(e)
            }
            .subscribe {
                suspend.resume(LCResponse(SUCCESS_CODE, "", true)) {}
            }
    }
}

/**
 * 批量更新数据 - 返回数组
 */
suspend fun <T : MyLCObject> lcSaveAll2(list: MutableList<T>): LCResponse<MutableList<T>> {
    return suspendCancellableCoroutine { suspend ->
        check(!list.isNullOrEmpty()) { "数据不能为空" }
        val listObject: MutableList<LCObject> = mutableListOf()
        for (t in list) {
            val lcObject = LCObject.createWithoutData(getClassName(t), t.objectId)
            put(lcObject, t, null)
            listObject.add(lcObject)
        }

        // 批量更新
        var observable = LCObject.saveAllInBackground(listObject)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .doOnError { e ->
                suspend.resumeWithException(e)
            }
            .subscribe {
                suspend.resume(LCResponse(SUCCESS_CODE, "", list)) {}
            }
    }
}


/**
 * 获取数据
 */
suspend fun <T : MyLCObject> lcQuery(
    query: LCQuery<*>,
    tClass: Class<T>
): LCResponse<MutableList<T>> {
    return suspendCancellableCoroutine { suspend ->
        query.className = getClassName(tClass)
        //格式转换
        val gson = Gson()
        val observable = query.findInBackground()
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .doOnError { e ->
                suspend.resumeWithException(e)
            }
            .subscribe { lcObjects ->
                val list: MutableList<T> = mutableListOf()
                for (lcObject in lcObjects) {
                    val json = gson.toJson(lcObject.serverData)
                    list.add(gson.fromJson(json, tClass))
                }
                suspend.resume(LCResponse(SUCCESS_CODE, "", list)) {}
            }
    }
}


/**
 * 获取单条数据
 */
suspend fun <T : MyLCObject> lcQuerySingle(query: LCQuery<*>, tClass: Class<T>): LCResponse<T> {
    var data: T?
    return suspendCancellableCoroutine { suspend ->
        query.limit(1)
        query.className = getClassName(tClass)
        //格式转换
        val gson = Gson()
        val lcObjects = query.findInBackground()
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .doOnError { e ->
                suspend.resumeWithException(e)
            }
            .subscribe { listOb ->
                val list: MutableList<T> = mutableListOf()
                for (lcObject in listOb) {
                    val json = gson.toJson(lcObject.serverData)
                    list.add(gson.fromJson(json, tClass))
                }
                data = if (list.isNullOrEmpty()) {
                    null
                } else {
                    list[0]
                }
                suspend.resume(LCResponse(SUCCESS_CODE, "", data)) {
                }
            }

    }
}

/**
 * 删除一条数据
 */
suspend fun <T : MyLCObject> lcDel(t: T): LCResponse<Boolean> {
    return suspendCancellableCoroutine { suspend ->
        val todo = LCObject.createWithoutData(getClassName(t), t.objectId)
        //接口删除
        var observable = todo.deleteInBackground()
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .doOnError { e ->
                suspend.resumeWithException(e)
            }
            .subscribe {
                suspend.resume(LCResponse(SUCCESS_CODE, "", true)) {}
            }
    }
}

/**
 * 批量删除
 */
suspend fun <T : MyLCObject> lcDel(list: MutableList<T>): LCResponse<Boolean> {
    return suspendCancellableCoroutine { suspend ->
        check(!list.isNullOrEmpty()) { "数据不能为空" }
        val listObject: MutableList<LCObject> = mutableListOf()
        for (t in list) {
            val lcObject = LCObject.createWithoutData(getClassName(t), t.objectId)
            listObject.add(lcObject)
        }
        // 批量删除
        var observable = LCObject.deleteAllInBackground(listObject)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .doOnError { e ->
                suspend.resumeWithException(e)
            }
            .subscribe {
                suspend.resume(LCResponse(SUCCESS_CODE, "", true)) {}
            }
    }
}

/**
 * 统计数量
 */
suspend fun <T : MyLCObject> lcCountInBackground(
    query: LCQuery<LCObject>,
    tClass: Class<T>
): LCResponse<Int> {
    return suspendCancellableCoroutine { suspend ->
        query.className = getClassName(tClass)
        var observable = query.countInBackground()
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .doOnError { e ->
                suspend.resumeWithException(e)
            }
            .subscribe { count ->
                suspend.resume(LCResponse(SUCCESS_CODE, "", count)) {}
            }
    }
}

/**
 * 获取推送用Installation值
 */
suspend fun lcGetCurrentInstallation(): LCResponse<String> {
    return suspendCancellableCoroutine { suspend ->
        //先上传推送
        var observable = LCInstallation.getCurrentInstallation().saveInBackground()
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .doOnError { e ->
                suspend.resumeWithException(e)
            }
            .subscribe {
                //再获取推送id
                var installationId = LCInstallation.getCurrentInstallation().installationId
                suspend.resume(LCResponse(SUCCESS_CODE, "", installationId)) {}
            }
    }

}


/**
 * 私信
 */
suspend fun <T> lcSendMessageInBackground(
    pushId: String,
    pushData: PushData<T>
): LCResponse<Boolean> {
    return suspendCancellableCoroutine { suspend ->
        var pushQuery: LCQuery<LCInstallation> = LCInstallation.getQuery()
        pushQuery.whereEqualTo("installationId", pushId)
        val push = Push()
        val channels: MutableList<String> = java.util.ArrayList()
        channels.add("public")
        push.channels = channels
        push.data = pushData
        val jsonObject = JSONObject.Builder.create(ThirdUtil.toMap(ThirdUtil.toJson(push)))
        var observable = LCPush.sendDataInBackground(jsonObject, pushQuery)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .doOnError { e ->
                suspend.resumeWithException(e)
            }
            .subscribe {
                suspend.resume(LCResponse(SUCCESS_CODE, "", true)) {}
            }
    }
}

/**
 * 发送短信
 * @param phone
 * @param ttl 单位分钟
 * @param appName app名字
 * @param text 用于什么类型，如：登录
 */
suspend fun lcSendCode(
    phone: String,
    ttl: Int,
    appName: String,
    text: String
): LCResponse<Boolean> {
    return suspendCancellableCoroutine { suspend ->
        val option = LCSMSOption()
        option.setTtl(ttl) //10分钟
        option.setApplicationName(appName)
        option.setOperation(text)
        var observable = LCSMS.requestSMSCodeInBackground(phone, option)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .doOnError { e ->
                suspend.resumeWithException(e)
            }
            .subscribe {
                suspend.resume(LCResponse(SUCCESS_CODE, "", true)) {}
            }

    }

}

/**
 *  验证短信
 */
suspend fun lcVerifyCode(phone: String, code: String): LCResponse<Boolean> {
    return suspendCancellableCoroutine { suspend ->
        var observable = LCSMS.verifySMSCodeInBackground(code, phone)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .doOnError { e ->
                suspend.resumeWithException(e)
            }
            .subscribe {
                suspend.resume(LCResponse(SUCCESS_CODE, "", true)) {}
            }
    }

}

/***********************************下面是工具*****************************************/

private fun <T : MyLCObject> getClassName(t: T): String {
    return getClassName(t.javaClass)
}

/**
 * 1、优先取MyLCObject里面的getClassName  （动态表名）
 * 2、然后取注解@LCClassName 为表名
 * 3、最后取Class的名字
 */
private fun <T : MyLCObject> getClassName(mClass: Class<T>): String {
    val gson = Gson()
    var t = gson.fromJson("{}", mClass)
    val className = t?.className
    if (!className.isNullOrEmpty()) {
        return className
    }
    val lcName = mClass.getAnnotation(LCClassName::class.java) ?: return mClass.simpleName
    return lcName?.value.ifEmpty {
        mClass.simpleName
    }
}

/**
 * 数据组装
 */

private fun put(lcObject: LCObject, o: Any, incrementKey: String?) {
    try {
        /**获取所有成员变量 */
        val fields = getAllDeclaredFields(o.javaClass)
        var array =
            o.javaClass.getAnnotation(LCIgnoreProperties::class.java)?.value ?: arrayOf()//指定数组
        for (field in fields) {
            val key = field?.name
            if (key == LCObject.KEY_OBJECT_ID || key == LCObject.KEY_ACL || key == incrementKey
                || field?.getAnnotation(LCIgnore::class.java) != null
                || key in array
            ) {
                continue
            }
            field?.isAccessible = true
            val value = if (field?.getAnnotation(LCJson::class.java) != null) {
                //使用json串入参，好处是可以添加json注解处理
                var gson = GsonBuilder()
//                    .excludeFieldsWithoutExposeAnnotation()//禁止使用，每个字段必须加@Expose()注解，不然不会输出
                    //kotlin 使用@Transient替换，，java 使用transient 来忽略某个字段
                    .setPrettyPrinting()
                    .disableHtmlEscaping()
                    .create()
                var json = gson.toJson(field.get(o))
                var mObject = gson.fromJson(json, Object::class.java)
                mObject
            } else {
                //直接整个实体传入，其实也是字段缓存成json
                field?.get(o)
            }
            /**等于null不上传，如果要置空，如String 用空字符串 ""  */
            if (value != null) {
                lcObject.put(key, value)
            }
        }
    } catch (e: Exception) {
        e.printStackTrace()
    }
}


/**
 * 获取类的所有字段,包含父类
 *
 * @param clazz
 * @param <T>
 * @return
</T> */
fun <T> getAllDeclaredFields(clazz: Class<T>?): Array<Field?> {
    var clazz = clazz
    //子类、父类的列表，
    var fieldArrayList: MutableList<Array<Field>> = mutableListOf()
    while (clazz != null) {
        if (clazz == Any::class.java) {
            clazz = clazz.superclass as Class<T>?
            continue
        }
        fieldArrayList.add(clazz.declaredFields) //getDeclaredFields 只能获取当前类的所有字段
        //转换为父类，继续获取
        clazz = clazz.superclass as Class<T>?
    }
    var fieldCount = 0
    var fieldIndex = 0
    for (fieldArray in fieldArrayList) {
        fieldCount += fieldArray.size
    }
    var allFields = arrayOfNulls<Field>(fieldCount)
    //遍历类
    for (fieldArray in fieldArrayList) {
        //遍历当前类的所有字段
        for (field in fieldArray) {
            allFields[fieldIndex++] = field
        }
    }
    return allFields
}